home *** CD-ROM | disk | FTP | other *** search
/ Visual Basic Toolbox / Visual Basic Toolbox (P.I.E.)(1996).ISO / ole / ole2bm / ole2bm.bas next >
BASIC Source File  |  1994-09-22  |  14KB  |  293 lines

  1. '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  2. ' OLE2BM.BAS ver. 1.3           VB 3.0 Pro Module                rev. 9/23/94
  3. '____________________________________________________________________________
  4. '
  5. ' The VB 3.0 Pro code in this module provides a way to transfer bitmap data
  6. ' back and forth between a bitmap object within an OLE 2.0 control (that's
  7. ' MSOLE2.VBX, not OLECLIENT.VBX!) and a picture box on a container form such
  8. ' that the user can edit the bitmap manually in PaintBrush along the way.
  9. '
  10. ' This capability is useful when you wish to draw certain bitmap elements
  11. ' programmatically before or after hand editing.
  12. '
  13. ' The considerable effort required in the support procedures below is quite
  14. ' typical of the wall one hits in attempting to gain programmatic control
  15. ' over data in embedded OLE 2.0 objects under VB.  Getting the data into the
  16. ' OLE2 control is relatively easy--getting it out is the hard part.
  17. '
  18. ' If you know a simpler way to get the data out, or if you understand why some
  19. ' bitmap colors appear more muted in the OLE2 control's display than in the
  20. ' picture box, I'd love to hear from you!
  21. '
  22. ' Version 1.3 is more robust than earlier releases because
  23. '
  24. '  1. The function OleFile2Picture() now buffers bitmap data read from disk in
  25. '     a huge VB long integer array rather than a in VB string, thus lifting the
  26. '     64K limit on the bitmaps it can extract from OLE2 files. Available memory
  27. '     is now the only =realistic= limit; the absolute limit of 8 GB imposed by
  28. '     the largest array index a long integer can specify shouldn't pose much of
  29. '     a problem for the foreseeable future.  The array technique banks on the
  30. '     fact that the image data in a Win 3.x DIB always starts on a dword boundary.
  31. '  2. OleFile2Picture() makes =no= assumptions about class or object names in
  32. '     the temporary OLE2 file header.  Apparently, such names may be absent.
  33. '     Instead, OleFile2Picture() simply finds the first valid embedded bitmap
  34. '     in the OLE2 file.
  35. '  3. The tests for bitmap validity in OLEFile2Picture() have been tightened up
  36. '     since version 1.2, but I'm sure they could be more rigorous.
  37. '
  38. '
  39. '   Jeremy McCreary
  40. '   Cliffshade Computing
  41. '   CIS [72341,3716]
  42. '____________________________________________________________________________
  43.  
  44. Option Explicit
  45. DefInt A-Z
  46.  
  47. '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  48. ' Bitmap-related constants and data structures
  49. '____________________________________________________________________________
  50.  
  51. Global Const OLE_CREATE_EMBED = 0   ' Ole control .Action settings
  52. Global Const OLE_ACTIVATE = 7
  53. Global Const OLE_SAVE_TO_FILE = 11
  54.  
  55. Global Const OLE_CHANGED = 0        ' Ole control .Updated event code
  56.  
  57. Global Const SRCCOPY = &HCC0020     ' BitBlt raster op: Overwrite destination
  58.  
  59. Global Const CBM_INIT = &H4&        ' Init created DIB with the data passed
  60. Global Const DIB_RGB_COLORS = 0     ' DIB file color tables use RGB values
  61.  
  62. Type BitmapFileHeaderType ' File header common to =all= Win 3.x .BMP files
  63.   bfType      As Integer  ' Always contains bitmap ID string "BM"
  64.   bfSize      As Long     ' Bitmap file size in bytes, including this header
  65.   bfReserved1 As Integer  ' Always null
  66.   bfReserved2 As Integer  ' Always null
  67.   bfOffBits   As Long     ' Offset from =start= of this header to start of data
  68. End Type
  69.  
  70.  
  71. '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  72. ' Data structures and variables for CVL().
  73. '____________________________________________________________________________
  74. Type LongType
  75.     Numeric As Long
  76. End Type
  77.  
  78. Type String4Type
  79.     bytes As String * 4
  80. End Type
  81.  
  82. Dim LongInt As LongType                ' Declare at module level for speed
  83. Dim LongString As String4Type
  84.  
  85.  
  86. '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  87. ' Required Windows 3.1 API declarations in type-safe form.
  88. '____________________________________________________________________________
  89.  
  90. Declare Function BitBlt Lib "GDI" (ByVal DesthDC, ByVal DestX, ByVal DestY, ByVal DestWidth, ByVal DestHeight, ByVal SourcehDC, ByVal SourceX, ByVal SourceY, ByVal ROP As Long)
  91. Declare Function CreateCompatibleDC Lib "GDI" (ByVal hDC)
  92. Declare Function CreateDIBitmapPacked Lib "GDI" Alias "CreateDIBitmap" (ByVal hDC, lpPackedDIB As Long, ByVal InitFlag&, lpDataBits As Long, lpBitmapInfo As Long, ByVal ColorUse)
  93. Declare Function DeleteDC Lib "GDI" (ByVal hDC)
  94. Declare Function DeleteObject Lib "GDI" (ByVal hObj)
  95. Declare Function GetTempFileName Lib "Kernel" (ByVal DriveLetterAscii, ByVal PrefixName$, ByVal Unique, ByVal NameBuffer$)
  96. Declare Function SelectObject Lib "GDI" (ByVal hDC, ByVal hObject)
  97.  
  98. '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  99. ' Convert a 4-byte hexadecimal string to a long integer using type coercion.
  100. '____________________________________________________________________________
  101. Function CVL (bcd$) As Long
  102.  
  103.   LongString.bytes = bcd$
  104.   LSet LongInt = LongString           ' Transfer 4 bytes between structures
  105.   CVL = LongInt.Numeric               ' Data now in numeric format
  106.  
  107. End Function
  108.  
  109. '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  110. ' Transfer an embedded bitmap object from an OLE 2.0 (MSOLE2.VBX) control to
  111. ' a VB picture box via the intermediaries of a temporary OLE file and a
  112. ' packed DIB memory structure.
  113. '____________________________________________________________________________
  114. Sub Ole2Pic (pic As PictureBox, ole As Control)
  115. Dim f, h0, hbm, hmem, hpic, r
  116. Dim file$
  117.  
  118.   file$ = TempFileName$("")       ' Open a temporary OLE file
  119.   f = FreeFile
  120.   Open file$ For Binary As f
  121.   ole.FileNumber = f              ' Make its handle the save destination
  122.   ole.Action = OLE_SAVE_TO_FILE   ' Save the embedded data as an OLE 2.0 file
  123.   Close f
  124.  
  125.   hbm = OLEFile2Picture(pic, file$) ' Extract the bitmap from the OLE file
  126.   If hbm Then                     ' Copy the extracted DDB into picture box
  127.     hpic = pic.hDC
  128.     hmem = CreateCompatibleDC(hpic)
  129.     h0 = SelectObject(hmem, hbm)  ' Select the DDB into the memory DC
  130.     r = BitBlt(hpic, 0, 0, CInt(pic.ScaleWidth), CInt(pic.ScaleHeight), hmem, 0, 0, SRCCOPY)
  131.     r = SelectObject(hmem, h0)    ' Restore the object previously selected
  132.     r = DeleteObject(hbm)         ' Recover system resources
  133.     r = DeleteDC(hmem)
  134.     pic.Refresh                   ' Update the screen now
  135.   End If
  136.   
  137.   Kill file$                      ' Waste the temporary OLE file
  138.  
  139. End Sub
  140.  
  141. '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  142. ' Copy the 1st device-independent bitmap (DIB) found in a possibly compound
  143. ' OLE 2.0 file to a packed DIB memory image, create a device-dependent bitmap
  144. ' (DDB) from the packed DIB, and return the DDB handle for future reference
  145. ' if successful, 0 if not.
  146. '
  147. ' NB: Once the DDB is created (i.e., once the packed DIB color table has been
  148. ' translated to the nearest available device-specific colors), subsequent
  149. ' display of the bitmap goes =much= faster than if displayed directly as a
  150. ' packed DIB, say with StretchDIBits().
  151. '____________________________________________________________________________
  152. Function OLEFile2Picture (pic As PictureBox, OLEfile$)
  153. Dim hbm, hOLE, k, valid
  154. Dim jj As Long, kk As Long
  155. Dim bfhLen As Long, buffers As Long, bytes As Long, flength As Long
  156. Dim ptr As Long, remainder As Long, start As Long
  157. Dim BitmapOffset As Long
  158. Dim buffer$
  159. Dim bfh As BitmapFileHeaderType
  160. Const BUFFER_SIZE = 2048& * 4&          ' File input buffer length must end on
  161. Const STRING_LIMIT = 65500              '   dword boundary
  162. Const MB = 16                           ' Stop style MsgBox
  163. Const BitmapID$ = "BM"
  164. Const MAX_BMINFO_SIZE = 40& + 256& * 4& ' BitmapInfo header for 256-color bitmap
  165. Const MIN_BITMAP_SIZE = 14& + 40& + 16& * 4& + 2& ' Assume < 24-bit graphics
  166.  
  167.   bfhLen = Len(bfh)
  168.  
  169.   hOLE = FreeFile                       ' Open the source OLE file
  170.   Open OLEfile$ For Binary As hOLE
  171.   flength = LOF(hOLE)
  172.   If flength <= MIN_BITMAP_SIZE Then    ' File too small to hold a bitmap
  173.     MsgBox "Sorry, your OLE2 file is too small to contain a bitmap.", MB, "OLE2 File Error"
  174.     GoTo OLEFile2PictureExit
  175.   End If
  176.   
  177.   start = 1&                             ' Start at 1st byte of file
  178.   bytes = flength
  179.  
  180.   Do                                     ' Search for 1st/next bitmap ID string
  181.     buffers = bytes \ BUFFER_SIZE
  182.     buffer$ = Space$(BUFFER_SIZE)
  183.     Seek hOLE, start                     ' Set file pointer for reading start
  184.     For k = 1 To buffers
  185.       Get hOLE, , buffer$                ' Read a bufferfull of OLE file data
  186.       ptr = InStr(buffer$, BitmapID$)    ' Look for a possible bitmap file header
  187.       If ptr Then Exit For Else BitmapOffset = BitmapOffset + BUFFER_SIZE
  188.     Next
  189.     If ptr = 0 Then                      ' Check the tail
  190.       remainder = bytes Mod BUFFER_SIZE
  191.       buffer$ = Space$(remainder)        ' Now get what's left
  192.       Get hOLE, , buffer$
  193.       ptr = InStr(buffer$, BitmapID$)    ' Look one last time
  194.     End If
  195.     If ptr Then                          ' Check for a valid bitmap file header
  196.       BitmapOffset = BitmapOffset + ptr
  197.       Get hOLE, BitmapOffset, bfh        ' Read the bitmap file header
  198.       bytes = bfh.bfSize - bfhLen        ' Calculate bitmap size
  199.       valid = ((bytes > MAX_BMINFO_SIZE) And (BitmapOffset + bfhLen + bytes <= flength + 1&) And (bfh.bfOffBits <= bfhLen + MAX_BMINFO_SIZE) And (bfh.bfReserved1 * bfh.bfReserved2 = 0))
  200.       If valid Then                      ' Header contents look reasonable for a bitmap
  201.         ReDim PackedDIB(bytes / 4&) As Long ' Initialize dynamic array for packed DIB
  202.         buffer$ = Space$(BUFFER_SIZE)
  203.         buffers = bytes \ BUFFER_SIZE    ' Number of buffers needed to read bitmap
  204.         remainder = bytes Mod BUFFER_SIZE
  205.         ptr = 1&                         ' ptr -> 1st byte of bitmapinfo header
  206.         jj = 0&                          ' jj -> next array element to fill
  207.         Do Until ptr > bytes - remainder ' Build up a packed DIB memory image
  208.           Get hOLE, , buffer$            '  a VB array, 1 bufferfull at a time
  209.           For kk = 1& To BUFFER_SIZE - 3& Step 4&  ' Copy buffer to array
  210.             PackedDIB(jj) = CVL(Mid$(buffer$, kk, 4))  ' kk -> dword to copy
  211.             jj = jj + 1&
  212.           Next
  213.           ptr = ptr + BUFFER_SIZE        ' ptr -> next file byte to read
  214.         Loop
  215.         buffer$ = Space$(remainder)      ' Now get what's left
  216.         Get hOLE, , buffer$
  217.         kk = remainder Mod 4&            ' Pad buffer to dword boundary
  218.         If kk Then buffer$ = buffer$ & String$(4& - kk, 0)
  219.         For kk = 1& To remainder - 3& Step 4&  ' Copy buffer to array
  220.           PackedDIB(jj) = CVL(Mid$(buffer$, kk, 4)) ' kk -> dword to copy
  221.           jj = jj + 1&
  222.         Next
  223.         ptr = (bfh.bfOffBits - bfhLen) \ 4&  ' Array element starting DIB data bits
  224.         ' Create a device-dependent bitmap (DDB) compatible with the target
  225.         ' picture box device context.
  226.         hbm = CreateDIBitmapPacked(pic.hDC, PackedDIB(0), CBM_INIT, PackedDIB(ptr), PackedDIB(0), DIB_RGB_COLORS)
  227.         Exit Do                          ' Done--extracted first valid bitmap
  228.       Else                               ' Try again--this is no embedded bitmap header!
  229.         start = BitmapOffset + Len(BitmapID$) ' Skip over bogus bitmap ID string
  230.         bytes = flength - start          ' Re-calculate remaining bytes
  231.       End If
  232.     Else                                 ' Done--no bitmap candidates found
  233.       valid = False
  234.       Exit Do
  235.     End If
  236.   Loop While bytes > MIN_BITMAP_SIZE
  237.   
  238.   If Not valid Then
  239.       MsgBox "Sorry, couldn't find an embedded bitmap within your temporary OLE2 file.", MB, "OLE2 File Error"
  240.   End If
  241.  
  242. OLEFile2PictureExit:
  243.   Close hOLE                             ' Done with the OLE file
  244.   OLEFile2Picture = hbm                  ' Pass back 0 if failed, DDB handle otherwise
  245.  
  246. End Function
  247.  
  248. '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  249. ' Embed the bitmap contained within a VB picture box in an OLE 2.0 control
  250. ' (MSOLE2.VBX) via a temporary .BMP file.
  251. '
  252. ' NB: The OLE control =requires= the .SourceDoc file to have the extension
  253. ' "BMP" in order to embed its data as a PaintBrush object.
  254. '____________________________________________________________________________
  255. Sub Pic2Ole (pic As PictureBox, ole As Control)
  256. Dim r
  257. Dim file$
  258.  
  259.   file$ = TempFileName$("BMP")   ' Get a temporary file name with .BMP ext.
  260.   SavePicture pic.Image, file$   ' Save the picture box bitmap as a DIB file
  261.   ole.Class = "PBrush"           ' Specify creation of Pbrush bitmap object
  262.   ole.SourceDoc = file$          ' Make the temporary file the data source
  263.   ole.Action = OLE_CREATE_EMBED  ' Embed the data as an OLE 2.0 object
  264.   Kill file$                     ' Waste the temporary file
  265.  
  266. End Sub
  267.  
  268. '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  269. ' Create a temporary file, which will live briefly in the subdirectory
  270. ' specified by the user's TEMP environment variable--with luck perhaps
  271. ' on a ram drive for speed.
  272. '____________________________________________________________________________
  273. Function TempFileName$ (ext$)
  274. Dim r
  275. Dim file$
  276. Const DOT = 46                            ' ANSI code for period
  277.  
  278.   file$ = Space$(255)                     ' Allow plenty of room for the name
  279.   r = GetTempFileName(0, "", -1, file$)   ' Let Windows supply a name
  280.   file$ = Trim(file$)                     ' Strip off any excess white space
  281.   If Len(ext$) Then                       ' Replace the .TMP extension
  282.     r = InStr(file$, ".TMP")              ' Find the .TMP extension
  283.     If r Then                             ' Replace if present
  284.       If Asc(ext$) <> DOT Then r = r + 1  ' Does ext. passed include period?
  285.       Mid$(file$, r) = ext$               ' Replace .TMP with new extension
  286.     End If
  287.   End If
  288.  
  289.   TempFileName$ = file$                   ' Pass back the temporary file name
  290.  
  291. End Function
  292.  
  293.